package com.zhiwei.version.git.service.impl;

import com.zhiwei.version.constant.GitConstants;
import com.zhiwei.version.git.entity.GitBranchInfo;
import com.zhiwei.version.git.service.GitBranchService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.CreateBranchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.List;

/**
 * function
 * author zhiwei.yang
 * time 2020-3-4-15:34
 */
@Service
@Slf4j
public class GitBranchServiceImpl implements GitBranchService {

    @Override
    public String getCurrentBranch(Git git) throws IOException {
        return git.getRepository().getBranch();
    }

    @Override
    public GitBranchInfo getBranchList(Git git) throws GitAPIException {
        GitBranchInfo gitBranchInfo = new GitBranchInfo();
        List<Ref> branchRefList = git.branchList().setListMode(ListBranchCommand.ListMode.ALL).call();
        if (CollectionUtils.isEmpty(branchRefList)) {
            return gitBranchInfo;
        }
        branchRefList.forEach(ref -> {
            if (ref.getName().contains(GitConstants.localBranchPrefix)) {
                gitBranchInfo.getLocalBranchList().add(ref.getName());
                gitBranchInfo.getLocalBranchRefMap().put(Repository.shortenRefName(ref.getName()), ref);
            }
            if (ref.getName().contains(GitConstants.remoteBranchPrefix)) {
                gitBranchInfo.getRemoteBranchList().add(ref.getName());
                gitBranchInfo.getRemoteBranchRefMap().put(Repository.shortenRefName(ref.getName()), ref);
            }
        });
        return gitBranchInfo;
    }

    @Override
    public boolean pullBranch(Git git, String branch) throws GitAPIException, IOException {
        if (checkoutBranch(git, branch)) {
            git.pull().call();
            return true;
        }
        return false;
    }

    @Override
    public boolean checkoutBranch(Git git, String branch) throws GitAPIException, IOException {
        return this.checkoutBranch(git, branch, false);
    }

    @Override
    public boolean checkoutBranch(Git git, String branch, boolean force) throws GitAPIException, IOException {

        //当前工作分支相同，无需切换
        if(getCurrentBranch(git).equals(branch)){
            return true;
        }

        GitBranchInfo gitBranchInfo = getBranchList(git);
        //存在分支本地
        if (gitBranchInfo.isExistLocalBranch(branch)) {
            git.checkout().setCreateBranch(false).setName(branch).call();
            return true;
        }

        //本地分支不存在，无需强制创建，返回false
        if (!force) {
            return false;
        }

        //不存在本地分支, 远程分支存在则创建，否则本地创建
        if (gitBranchInfo.isExistRemoteBranch(branch)) {
            git.checkout()
                    .setCreateBranch(true)
                    .setName(branch)
                    .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
                    .setStartPoint(GitConstants.remoteBranchNamePrefix.concat(branch))
                    .call();
            return true;
        }
        git.checkout().setCreateBranch(true).setName(branch).call();
        return true;
    }

    @Override
    public boolean mergeBranch(Git git, String targetBranch, String sourceBranch) throws GitAPIException, IOException {
        if(StringUtils.isEmpty(targetBranch) || StringUtils.isEmpty(sourceBranch)){
            return false;
        }
        GitBranchInfo gitBranchInfo = getBranchList(git);
        if(!gitBranchInfo.isExistLocalBranch(targetBranch) || !gitBranchInfo.isExistLocalBranch(sourceBranch)){
            return false;
        }
        if(!getCurrentBranch(git).equals(targetBranch)){
            this.checkoutBranch(git, targetBranch);
        }
        Ref sourceBranchRef = gitBranchInfo.getLocalBranchRef(sourceBranch);
        MergeResult mergeResult = git.merge().include(sourceBranchRef).call();
        return CollectionUtils.isEmpty(mergeResult.getCheckoutConflicts());
    }
}
